/*
* INSANE - Interactive Structural Analysis Environment
*
* Copyright (C) 2003-2005
* Universidade Federal de Minas Gerais
* Escola de Engenharia
* Departamento de Engenharia de Estruturas
* 
* Author's email :    insane@dees.ufmg.br
* Author's website :  http://www.dees.ufmg.br/insane
* 
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
* 
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
package br.ufmg.dees.insane.draw;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Arc2D;
import java.awt.geom.Line2D;
import java.util.ListIterator;
import java.util.Vector;

public abstract class ArrowLineDraw extends Draw {
	
	protected double aValue;
	protected double bValue;
	
//*****************************************************************************
	
	/**
	 * Sets the load applied point A force.
	 *
	 * @param   fa  The desired force to be set.
	 */
	public void setAValue(double fa) {
		aValue = fa;
	}
	
//*****************************************************************************
	
	/**
	 * Returns the load applied force at point A.
	 *
	 * @return   The load applied force at point A
	 */
	public double getAValue() {
		return aValue;
	}
	
//*****************************************************************************
	
	/**
	 * Sets the load applied force at point B.
	 *
	 * @param   fb  The desired load applied force to be set.
	 */
	public void setBValue(double fb) {
		bValue = fb;
	}
	
//*****************************************************************************
	
	/**
	 * Returns the load applied force at point B.
	 *
	 * @return   The load applied force at point B.
	 */
	public double getBValue() {
		return bValue;
	}
	
//*****************************************************************************
	
	public Point getP1() {
		return this.getLocation();
	}
	
//*****************************************************************************
	
	public Point getP2() {
		Point po = new Point(this.getSize().width, this.getSize().height);
		return po;
	}
	
//*****************************************************************************
	
	public double getLength() {
		double dx = this.getX2() - this.getX1();
		double dy = this.getY2() - this.getY1();
		double l = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
		return l;
	}
	
//*****************************************************************************
	
	public double getX1() {
		return getLocation().x;
	}
	
//*****************************************************************************
	
	public double getY1() {
		return getLocation().y;
	}
	
//*****************************************************************************
	
	public double getX2() {
		return getSize().width;
	}
	
//*****************************************************************************
	
	public double getY2() {
		return getSize().height;
	}
	
//*****************************************************************************
	
	public double getAngle() {
		double theta;
		double dx = getX2() - getX1();
		double dy = getY2() - getY1();
		theta = Math.atan2(-dy, dx);
		return theta;
	}
	
//*****************************************************************************
	
	/**
	 * Returns the draw bounds in device units.
	 *
	 * @return the draw bounds
	 */
	public Rectangle getBounds() {
		int x1 = this.getLocation().x;
		int y1 = this.getLocation().y;
		int x2 = this.getSize().width;
		int y2 = this.getSize().height;
		Rectangle rect = new Rectangle(x1, y1, x2 - x1, y2 - y1);
		return rect;
	}
	
//*****************************************************************************
	
	public boolean contains(Point p) {
		int x1 = this.getLocation().x;
		int y1 = this.getLocation().y;
		int x2 = this.getSize().width;
		int y2 = this.getSize().height;
		Line2D.Double line = new Line2D.Double(x1, y1, x2, y2);
		return line.intersects(p.x - 3, p.y -3, 6, 6);
	}
	
//*****************************************************************************
	
	/**
	 * Draws in a given Graphics.
	 *
	 * @see   java.awt.Graphics
	 */
	public void draw(Graphics2D g2) {
		Vector shapes = getShapes();
		ListIterator shps = shapes.listIterator();
		while(shps.hasNext()){
			g2.draw( ((Shape)shps.next()) );
		}
		Color currentColor = g2.getColor();
		if (isSelected()) {
			g2.setColor( Color.red );
			shps = shapes.listIterator();
			while(shps.hasNext()){
				g2.draw( ((Shape)shps.next()) );
			}
		}
		g2.setColor(currentColor);
	}
	
//*****************************************************************************
	
	public static class X extends ArrowLineDraw {
		
	//*****************************************************************************
		
		public X(int x1, int y1, int x2, int y2 ) {
			this(new Point(x1, y1), new Point(x2, y2));
		}
		
	//*****************************************************************************
		
		public X(Point p1, Point p2) {
			this(p1, p2, null);
		}
		
	//*****************************************************************************
		
		public X(Point p1, Point p2, Object represents) {
			this.setLocation(p1);
			this.setSize(new Dimension(p2.x, p2.y));
			this.setRepresents(represents);
		}
		
	//*****************************************************************************
		
		public boolean equals(Object obj) {
			if (super.equals(obj) && obj instanceof ArrowLineDraw.X) {
				ArrowLineDraw d = (ArrowLineDraw)obj;
				if (d.getP2().equals(getP2())){
					return true;
				}
			}
			return false;
		}
		
	//*****************************************************************************
		
		public Vector<Object> getShapes() {
			Vector<Object> shapes = new Vector<Object>();
			int x1 = this.getLocation().x;
			int y1 = this.getLocation().y;
			int x2 = this.getSize().width;
			int y2 = this.getSize().height;
			double scale = this.getScale();
			int num = (int)(( this.getLocation() ).distance( new Point(x2,y2) )/15);
			if (num < 1) num = 1;
			for (int i=0; i<=num; i++) {
				double t = (double)i/num;
				int x = (int)((1-t)*x1+t*x2);
				int y = (int)((1-t)*y1+t*y2);
				double F = (1-t)*getAValue()+t*getBValue();
				double ARROW = 6;
				double MOD = 10;
				MOD = MOD * scale * Math.abs(F);
				if ( MOD < ARROW ) ARROW = MOD;
				if (F > 0) {
					shapes.add(new Line2D.Double(x + MOD, y, x + MOD - ARROW, y + (ARROW/2)));
					shapes.add(new Line2D.Double(x + MOD, y, x + MOD - ARROW, y - (ARROW/2)));
					shapes.add(new Line2D.Double(x, y, x + MOD, y));
				} else if (F < 0) {
					shapes.add(new Line2D.Double(x , y, x  + ARROW, y + (ARROW/2)));
					shapes.add(new Line2D.Double(x , y, x + ARROW, y - (ARROW/2)));
					shapes.add(new Line2D.Double(x, y, x + MOD, y ));
				}
			}
//			shapes.add(new Line2D.Double(x1, y1, x2, y2));
			return shapes;
		}
	}
	
//*****************************************************************************
	
	public static class Y extends ArrowLineDraw {
		public Y(int x1, int y1, int x2, int y2 ) {
			this(new Point(x1, y1), new Point(x2, y2));
		}
		
	//*****************************************************************************
		
		public Y(Point p1, Point p2) {
			this(p1, p2, null);
		}
		
	//*****************************************************************************
		
		public Y(Point p1, Point p2, Object represents) {
			this.setLocation(p1);
			this.setSize(new Dimension(p2.x, p2.y));
			this.setRepresents(represents);
		}
		
	//*****************************************************************************
		
		public boolean equals(Object obj) {
			if (super.equals(obj) && obj instanceof ArrowLineDraw.Y) {
				ArrowLineDraw d = (ArrowLineDraw)obj;
				if (d.getP2().equals(getP2())){
					return true;
				}
			}
			return false;
		}
		
	//*****************************************************************************
		
		public Vector<Object> getShapes() {
			Vector<Object> shapes = new Vector<Object>();
			int x1 = this.getLocation().x;
			int y1 = this.getLocation().y;
			int x2 = this.getSize().width;
			int y2 = this.getSize().height;
			double scale = this.getScale();
			int num = (int)(( this.getLocation() ).distance( new Point(x2,y2) )/15);
			if (num < 1) num = 1;
			for (int i=0; i<=num; i++) {
				double t = (double)i/num;
				int x = (int)((1-t)*x1+t*x2);
				int y = (int)((1-t)*y1+t*y2);
				double F = (1-t)*getAValue()+t*getBValue();
				double ARROW = 6;
				double MOD = 10;
				MOD = MOD * scale * Math.abs(F);
				if ( MOD < ARROW ) ARROW = MOD;
				if (F > 0) {
					shapes.add( new Line2D.Double(x, y - MOD, x + (ARROW/2), y - MOD + ARROW) );
					shapes.add( new Line2D.Double(x, y - MOD, x - (ARROW/2), y - MOD + ARROW) );
					shapes.add( new Line2D.Double(x, y, x, y - MOD) );
				} else if (F < 0) {
					shapes.add( new Line2D.Double(x, y, x + (ARROW/2), y - ARROW) );
					shapes.add( new Line2D.Double(x, y, x - (ARROW/2), y - ARROW) );
					shapes.add( new Line2D.Double(x, y, x, y - MOD) );
				}
			}
//			shapes.add(new Line2D.Double(x1, y1, x2, y2));
			return shapes;
		}
	}
	
//*****************************************************************************
	
	public static class Z extends ArrowLineDraw {
		public Z(int x1, int y1, int x2, int y2 ) {
			this(new Point(x1, y1), new Point(x2, y2));
		}
		
	//*****************************************************************************
		
		public Z(Point p1, Point p2) {
			this(p1, p2, null);
		}
		
	//*****************************************************************************
		
		public Z(Point p1, Point p2, Object represents) {
			this.setLocation(p1);
			this.setSize(new Dimension(p2.x, p2.y));
			this.setRepresents(represents);
		}
		
	//*****************************************************************************
		
		public boolean equals(Object obj) {
			if (super.equals(obj) && obj instanceof ArrowLineDraw.Z) {
				ArrowLineDraw d = (ArrowLineDraw)obj;
				if (d.getP2().equals(getP2())){
					return true;
				}
			}
			return false;
		}
		
	//*****************************************************************************
		
		public Vector<Object> getShapes() {
			Vector<Object> shapes = new Vector<Object>();
			int x1 = this.getLocation().x;
			int y1 = this.getLocation().y;
			int x2 = this.getSize().width;
			int y2 = this.getSize().height;
			double sc = this.getScale();
			int num = (int)(( this.getLocation() ).distance( new Point(x2,y2) )/15);
			if (num < 1) num = 1;
			for (int i=0; i<=num; i++) {
				double t = (double)i/num;
				int x = (int)((1-t)*x1+t*x2);
				int y = (int)((1-t)*y1+t*y2);
				double F = (1-t)*getAValue()+t*getBValue();
				double ARROW = 6;
				double MOD = 5;
				double W = MOD * 4;
				double H = MOD * 4;
				if ( MOD < ARROW ) ARROW = MOD;
				if (F > 0) {
					shapes.add( new Arc2D.Double(x - W/2*sc, y - H/2*sc, W*sc, H*sc, -75, 225, 0));
					shapes.add( new Line2D.Double(x - 0.433 * W*sc, y - H/4*sc, x - 0.43 * W*sc, y - 0.5 * H*sc));
					shapes.add( new Line2D.Double(x - 0.433 * W*sc, y - H/4*sc, x - 0.25 * W*sc, y - 0.28 * H*sc));
				} else if (F < 0) {
					shapes.add( new Arc2D.Double( x - W/2*sc, y - H/2*sc, W*sc, H*sc, -75, 225, 0));
					shapes.add( new Line2D.Double( x + W/8*sc, y + H/2*sc, x + 0.2 * W*sc, y + 0.3 * H*sc));
					shapes.add( new Line2D.Double( x + W/8*sc, y + H/2*sc, x + 0.325 * W*sc, y + 0.5 * H*sc));
				}
			}
			//shapes.add(new Line2D.Double(x1, y1, x2, y2));
			return shapes;
		}
	}
	
//*****************************************************************************
}
